{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "实践建议\n",
    "- 使用 `emplace` 可以减少不必要的临时对象，尤其适用于复杂对象或不可复制的类型。\n",
    "- 需要注意容器可能触发的重分配，`emplace` 并非对所有容器都能完全避免移动甚至拷贝（如 `vector` 扩容时）。\n",
    "- 对于关联容器（`map`/`set`），`emplace` 能避免无意义的 `std::pair` 构造，更自然地传递键和值的构造参数。\n",
    "- 当语义是“把现有对象放进容器”，用 `push`/`insert` 仍然清晰；当语义是“在容器里构造新对象”，优先考虑 `emplace`。\n",
    "\n",
    "通过 `emplace` 系列，C++11 及之后的标准库在性能与语义清晰度上取得了双赢，成为现代 C++ 编码实践中非常值得掌握的工具。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 C++11 之前，标准容器的插入接口主要依赖 `push_back`、`insert` 等函数：用户必须先创建一个完整的对象，再把它“复制”或“移动”进容器。随着 **右值引用**、**完美转发**、**变参模板** 等语言特性的加入，标准库可以让容器直接在其内部存储空间中构造对象，避开无谓的临时对象创建与拷贝/移动成本，这就是 `emplace` 系列函数诞生的动机。\n",
    "\n",
    "自 C++11 起，大部分容器与容器适配器都提供了对应的 `emplace` 接口，如：\n",
    "\n",
    "- 顺序容器：`vector::emplace_back`、`list::emplace_front` 等；\n",
    "- 关联容器：`map::emplace`、`set::emplace_hint`；\n",
    "- 容器适配器：`stack::emplace`、`queue::emplace`、`priority_queue::emplace` 等；\n",
    "- 智能指针、`std::optional`、`std::variant` 等也提供了 `emplace` 接口。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`emplace` 的作用\n",
    "\n",
    "1. **原地构造（in-place construction）**<br>\n",
    "    直接在容器的内部存储上构造元素，避免了“先构造临时对象 → 再拷贝或移动”的过程。\n",
    "2. **性能提升**<br>\n",
    "    避免了临时对象的创建以及随后的拷贝/移动，尤其是对于昂贵或禁止拷贝/移动的类型尤为重要。\n",
    "3. **增强通用性**<br>\n",
    "    通过变参模板与完美转发，`emplace` 可以接受任意数量和类型的构造参数，使容器像构造函数一样地直接构造元素。\n",
    "4. **语义更清晰**<br>\n",
    "    表示“在容器内部构造一个对象”，而非“插入已有对象”，从意图上更加明确。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与 `push` / `insert` 的区别\n",
    "\n",
    "| 接口                 | 需要现成对象吗        | 构造位置              | 可能的拷贝/移动                             | 返回值               |\n",
    "| -------------------- | --------------------- | --------------------- | ------------------------------------------- | -------------------- |\n",
    "| `push_back`          | 是                    | 容器内部（复制/移动） | 1 次拷贝/移动                               | `void` / `reference` |\n",
    "| `insert` (顺序容器)  | 是或 initializer_list | 指定位置（复制/移动） | 可能拷贝/移动多次（尤其在 vector 重分配时） | 迭代器               |\n",
    "| `emplace_back`       | 否                    | 容器内部直接构造      | 0 次（若无重分配）                          | `reference`          |\n",
    "| `emplace` (关联容器) | 否                    | 节点内直接构造        | 0 次                                        | pair<iterator,bool>  |\n",
    "\n",
    "> 注意：对于 `std::vector` 等可能需要扩容的容器，如果扩容发生，即使使用 `emplace` 也仍不可避免内存搬迁以及已有元素的移动/复制。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**实现原理（概览）**\n",
    "\n",
    "关键语言特性\n",
    "- **变参模板**：`template<class... Args>`，允许 `emplace` 接受任意个构造参数。\n",
    "- **右值引用与完美转发**：`Args&&... args` 与 `std::forward<Args>(args)...`，保证参数以正确的值类别传递给目标构造函数。\n",
    "- **`allocator_traits::construct`**：在分配好的内存上调用对象构造函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "// 典型实现流程（以 `std::vector::emplace_back` 为例）\n",
    "\n",
    "template<class T, class Allocator>\n",
    "class vector {\n",
    "public:\n",
    "    template<class... Args>\n",
    "    reference emplace_back(Args&&... args) {\n",
    "        if (size() == capacity()) {\n",
    "            reallocate_and_move_elements(); // 可能触发扩容，并移动已有元素\n",
    "        }\n",
    "\n",
    "        // 在末尾位置直接构造元素\n",
    "        allocator_traits<Allocator>::construct(\n",
    "            alloc_, end_, std::forward<Args>(args)...);\n",
    "\n",
    "        ++size_;\n",
    "        return back();\n",
    "    }\n",
    "};"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. **容量检查**：若无剩余空间，需要扩容并迁移已有元素。\n",
    "2. **原地构造**：调用 `allocator_traits::construct`，其通常使用 `::new (ptr) T(args...)`（placement new）在目标位置直接构造。\n",
    "3. **完美转发**：`std::forward` 保证右值保持右值、左值保持左值，最大限度利用构造函数的重载匹配。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "节点式容器（如 `std::map`）\n",
    "- 节点容器会先按需分配节点内存，再使用相同的 `construct` 思想在节点内原地构造键值对。\n",
    "- 为优化构造过程，`std::map::emplace` 等还支持 `piecewise_construct` 与 `forward_as_tuple`，以分别传递键和值的构造参数，避免多余的 `std::pair` 构造。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "容器适配器（如 `std::stack`）\n",
    "- 适配器的 `emplace` 通常只是将调用转发给底层容器对应的 `emplace` 函数，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "cpp"
    }
   },
   "outputs": [],
   "source": [
    "template<class T, class Container>\n",
    "class stack {\n",
    "public:\n",
    "    template<class... Args>\n",
    "    decltype(auto) emplace(Args&&... args) {\n",
    "        c.emplace_back(std::forward<Args>(args)...);\n",
    "        return top();\n",
    "    }\n",
    "private:\n",
    "    Container c;\n",
    "};"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "其他容器/类\n",
    "- `std::optional::emplace`、`std::unique_ptr::reset` 等都借助同样的模式：销毁旧值（若有），然后通过完美转发直接在内存中构造新对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [],
   "source": [
    "! g++ -O3 benchmark_vector_emplace.cpp -lbenchmark -lpthread -o benchmark_vector_emplace && ./benchmark_vector_emplace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [],
   "source": [
    "! g++ -O3 benchmark_map_emplace.cpp -lbenchmark -lpthread -o benchmark_map_emplace && ./benchmark_map_emplace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "vscode": {
     "languageId": "shellscript"
    }
   },
   "outputs": [],
   "source": [
    "! g++ -O3 benchmark_priority_queue_emplace.cpp -lbenchmark -lpthread -o benchmark_priority_queue_emplace && ./benchmark_priority_queue_emplace"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "C++17",
   "language": "C++17",
   "name": "xcpp17"
  },
  "language_info": {
   "name": "C++17"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
